/*
 * Copyright (C) 2011 JiangHongTiao
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package sk.lieskove.jianghongtiao.paris.webclient.export;

import java.util.logging.Level;
import org.apache.log4j.Logger;
import sk.lieskove.jianghongtiao.common.utils.FileUtils;
import sk.lieskove.jianghongtiao.paris.webclient.persistence.ClassificationItem;
import weka.core.Attribute;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.converters.ArffSaver;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import sk.lieskove.jianghongtiao.commonpersist.Serialize;
import sk.lieskove.jianghongtiao.paris.core.classify.old.ClassifyManager;
import weka.core.DenseInstance;
import weka.core.FastVector;

/**
 * 
 * @author xjuraj e-mail: jjurco.sk_gmail.com
 */
public class ArffExport implements Serializable {

    private static transient Logger log = Logger.getLogger(ArffExport.class);

    /**
     * check for final type (class) of the document. If classes was put together
     * final type is different. otherwise it is normal type
     * @param joinItems joined types
     * @param pageClass original type (class) of the document (classified)
     * @return 
     */
    private static String getDocClass(Map<String, String> joinItems, String pageClass) {
        if (joinItems.containsKey(pageClass.toLowerCase())) {
            return joinItems.get(pageClass.toLowerCase());
        } else {
            return pageClass.toLowerCase();
        }
    }

    /**
     * put together original and joined Document Types (Classes)
     * @param docType original types
     * @param joinItems joined types
     * @return final set of classes
     */
    private static List<String> getDocTypes(List<String> docType, Map<String, String> joinItems) {
        Set<String> result = new HashSet<String>(joinItems.values());
        Set<String> keys = new HashSet<String>();
        for (String string : docType) {
            keys.add(string.toLowerCase());
        }
        keys.removeAll(joinItems.keySet());
        result.addAll(keys);
        return new ArrayList<String>(result);
    }

    /**
     * 
     * @param languages
     * @param docType
     * @param items
     * @param joinString
     * @return 
     */
    public static Instances createInstances(List<String> languages, List<String> docType,
            List<ClassificationItem> items, String joinString) {

        if ((items == null) || (items.isEmpty())) {
            return null;
        }

        Map<String, String> joinItems = ClassifyManager.joinItems(joinString);
        List<String> docTypes = getDocTypes(docType, joinItems);

        try {
            Instance i = new DenseInstance(3);
            Attribute pageClass = new Attribute("page_class", docTypes);
            Attribute pageLang = new Attribute("page_language", languages);
            Attribute pageContent = new Attribute("page_content", (List<String>) null);

            ArrayList<Attribute> atts = new ArrayList<Attribute>(3);
            atts.add(pageClass);
            atts.add(pageLang);
            atts.add(pageContent);

            // 3. fill with data
            Instances data = new Instances("Paris", atts, 0);
            for (ClassificationItem item : items) {

                String textData = FileUtils.read(new File(item.getFileLocation()));
                if (textData == null) {
                    textData = "null";
                }

                String lang = (item.getLanguageName() != null)
                        ? item.getLanguageName().name() : "null";
                String type = (item.getDocumentType() != null)
                        ? item.getDocumentType().name().toLowerCase() : "null";

                i.setValue(pageClass, docTypes.indexOf(getDocClass(joinItems, type)));
                i.setValue(pageLang, languages.indexOf(lang));
                i.setValue(pageContent, textData);

                data.add(i);
            }
            data.setClass(pageClass);
            return data;
        } catch (IOException ex) {
            log.error("Cannot save ARFF file.");
        }
        return null;
    }

    /**
     * 
     * @param languages
     * @param docType
     * @param items
     * @param joinString
     * @return 
     */
    public static Instances createInstances2(List<String> languages, List<String> docType,
            List<ClassificationItem> items, String joinString) {

        if ((items == null) || (items.isEmpty())) {
            return null;
        }

        Map<String, String> joinItems = ClassifyManager.joinItems(joinString);
        List<String> docTypes = getDocTypes(docType, joinItems);

        try {
            FastVector atts;
            FastVector attsRel;
            FastVector classes;
            FastVector langs;
            FastVector attValsRel;
            Instances data;
            Instances dataRel;
            double[] vals;
            double[] valsRel;
            int i;

            // 1. set up attributes
            atts = new FastVector();
            //document types
            classes = new FastVector();
            for (i = 0; i < docTypes.size(); i++) {
                classes.addElement(docTypes.get(i));
            }
            atts.addElement(new Attribute("page_class", classes));
            //document languages
            langs = new FastVector();
            for (i = 0; i < languages.size(); i++) {
                langs.addElement(languages.get(i));
            }
            atts.addElement(new Attribute("page_language", langs));
            // - string - page content
            atts.addElement(new Attribute("page_content", (FastVector) null));
            
            // 2. create Instances object
            data = new Instances("Paris", atts, 0);
            for (ClassificationItem item : items) {

                String textData = FileUtils.read(new File(item.getFileLocation()));
                if (textData == null) {
                    textData = "null";
                }
                vals = new double[data.numAttributes()];
                vals[0] = classes.indexOf(item.getDocumentType().name());
                vals[1] = ;

            // 3. fill with data
            // first instance
            
            // - numeric
            
            // - nominal
            vals[1] = attVals.indexOf("val3"); 
            // - string
            vals[2] = data.attribute(2).addStringValue("This is a string!");
            // - date
            vals[3] = data.attribute(3).parseDate("2001-11-09");
            // - relational
            dataRel = new Instances(data.attribute(4).relation(), 0);
            // -- first instance
            valsRel = new double[2];
            valsRel[0] = Math.PI + 1;
            valsRel[1] = attValsRel.indexOf("val5.3");
            dataRel.add(new Instance(1.0, valsRel));
            // -- second instance
            valsRel = new double[2];
            valsRel[0] = Math.PI + 2;
            valsRel[1] = attValsRel.indexOf("val5.2");
            dataRel.add(new Instance(1.0, valsRel));
            vals[4] = data.attribute(4).addRelation(dataRel);
            // add
            data.add(new Instance(1.0, vals));

            // second instance
            vals = new double[data.numAttributes()];  // important: needs NEW array!
            // - numeric
            vals[0] = Math.E;
            // - nominal
            vals[1] = attVals.indexOf("val1");
            // - string
            vals[2] = data.attribute(2).addStringValue("And another one!");
            // - date
            vals[3] = data.attribute(3).parseDate("2000-12-01");
            // - relational
            dataRel = new Instances(data.attribute(4).relation(), 0);
            // -- first instance
            valsRel = new double[2];
            valsRel[0] = Math.E + 1;
            valsRel[1] = attValsRel.indexOf("val5.4");
            dataRel.add(new Instance(1.0, valsRel));
            // -- second instance
            valsRel = new double[2];
            valsRel[0] = Math.E + 2;
            valsRel[1] = attValsRel.indexOf("val5.1");
            dataRel.add(new Instance(1.0, valsRel));
            vals[4] = data.attribute(4).addRelation(dataRel);
            // add
            data.add(new Instance(1.0, vals));

            data.setClass(pageClass);
            return data;
        } catch (IOException ex) {
            log.error("Cannot save ARFF file.");
        }
        return null;
    }

    public static File instances2File(Instances data, String fileName) {
        if (data == null) {
            return null;
        }
        System.out.println("-------------------------------------------");
        System.out.println(data);
        System.out.println("-------------------------------------------");
        try {
            File out = FileUtils.getTempFile("arff_" + fileName + "_", ".arff");

            Serialize.serialize(new File("/home/xjuraj/Desktop/instances.java.object"), data);

            ArffSaver saver = new ArffSaver();

            saver.setInstances(data);
//            saver.setFile(out);
//            saver.setDestination(out);   // **not** necessary in 3.5.4 and later
            saver.writeBatch();

            log.debug("ARFF file saved into: " + out.getAbsolutePath());
            return out;
        } catch (IOException ex) {
            java.util.logging.Logger.getLogger(ArffExport.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;

    }

    public static File exportAsArff(List<String> languages, List<String> docType,
            List<ClassificationItem> items, String query, int wordsToKeep,
            String joinString) {

        Instances data = createInstances(languages, docType, items, joinString);
        Instances modifiedData = ClassifyManager.modifyInstances(data, wordsToKeep);

        return instances2File(modifiedData, query);
    }
}
